package controllers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import repository.AuctionRepository;
import repository.CategoryRepository;
import repository.ItemRepository;
import repository.PurchaseRepository;
import validator.AdvancedSearchFormValidator;
import validator.AnswerFormValidator;
import validator.FilterFormValidator;
import validator.ItemFormValidator;
import validator.OfferFormValidator;
import validator.PurchaseFormValidator;
import validator.QuestionFormValidator;

import command.AdvancedSearchForm;
import command.AnswerForm;
import command.FilterForm;
import command.ItemForm;
import command.OfferForm;
import command.PurchaseForm;
import command.QuestionForm;

import domain.Auction;
import domain.Category;
import domain.Complaint;
import domain.Image;
import domain.Item;
import domain.Offer;
import domain.Purchase;
import domain.Question;
import domain.User;
import domain.exceptions.ComplaintMyItemException;
import domain.exceptions.RestrictionBuyingException;

@Controller
public class ItemController {
	private ItemRepository itemRepository;
	private CategoryRepository categoryRepository;
	private PurchaseRepository purchaseRepository;
	private AuctionRepository auctionRepository;
	private ItemFormValidator itemFormValidator;
	private FilterFormValidator filterFormValidator;
	private AdvancedSearchFormValidator advancedSearchFormValidator;
	private PurchaseFormValidator purchaseFormValidator;
	private OfferFormValidator offerFormValidator;
	private QuestionFormValidator questionFormValidator;
	private AnswerFormValidator answerFormValidator;

	@Autowired
	public ItemController(ItemRepository itemRepository, CategoryRepository categoryRepository,
			PurchaseRepository purchaseRepository, AuctionRepository auctionRepository, 
			ItemFormValidator itemFormValidator, AdvancedSearchFormValidator advancedSearchFormValidator, 
			FilterFormValidator filterFormValidator, PurchaseFormValidator purchaseFormValidator,
			OfferFormValidator offerFormValidator, QuestionFormValidator questionFormValidator,
			AnswerFormValidator answerFormValidator){
		this.itemRepository=itemRepository;
		this.categoryRepository=categoryRepository;
		this.purchaseRepository=purchaseRepository;
		this.auctionRepository=auctionRepository;
		this.itemFormValidator=itemFormValidator;
		this.filterFormValidator=filterFormValidator;
		this.advancedSearchFormValidator=advancedSearchFormValidator;
		this.purchaseFormValidator=purchaseFormValidator;
		this.offerFormValidator=offerFormValidator;
		this.questionFormValidator = questionFormValidator;
		this.answerFormValidator = answerFormValidator;
	}

	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView ModifyItems(@RequestParam("itemid") Item item){
		ModelAndView mav = new ModelAndView();
		ItemForm itemForm = new ItemForm(item);
		List<Category> categories = categoryRepository.getCategories();

		mav.addObject("itemForm", itemForm);
		mav.addObject("itemImage", item.getImage());
		mav.addObject("categories", categories);
		mav.addObject("title", "Modify item");
		mav.addObject("itemid", item.getId());
		mav.addObject("action", "../item/ModifyItems");
		mav.setViewName("item/AbmItems");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	protected ModelAndView ModifyItems(@RequestParam("itemid")Item item, HttpSession session, ItemForm itemForm, 
			Errors errors){
		ModelAndView mav = new ModelAndView();
		itemFormValidator.validate(itemForm, errors);
		if (errors.hasErrors()) {
			mav.addObject("categories", categoryRepository.getCategories());
			return mav;
		}

		itemForm.update(item);
		return itemDetail(item, session);
	}

	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView AbmItems(HttpSession session){
		ModelAndView mav = new ModelAndView();

		mav.addObject("itemForm", new ItemForm());
		mav.addObject("action", "../item/AbmItems");
		mav.addObject("categories", categoryRepository.getCategories());
		mav.setViewName("item/AbmItems");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	protected ModelAndView AbmItems(HttpSession session, ItemForm itemForm, Errors errors){
		ModelAndView mav = new ModelAndView();
		itemFormValidator.validate(itemForm, errors);
		if (errors.hasErrors()) {
			mav.addObject("categories", categoryRepository.getCategories());
			return mav;
		}
		User user = (User)session.getAttribute("user"); 	
		Calendar cal = Calendar.getInstance();
		java.sql.Date jsqlD = new java.sql.Date( cal.getTime().getTime() );
		Image image;
		try {
			image = new Image(itemForm.getImage().getBytes());
		} catch (IOException e) {
			throw new RuntimeException("Error al leer el archivo de la imagen en el converter");
		}			
		Item item = new Item(itemForm.getName(), itemForm.getPrecio(), itemForm.getDescription(), jsqlD, itemForm.getCount(), 
				itemForm.getNewItem(), user, itemForm.getCategory(), itemForm.getPriority(), itemForm.getType());
		item.setImage(image);
		itemRepository.newItem(item);
		Auction auction = new Auction(item, itemForm.getPrecio());
		auctionRepository.newAuction(auction);

		return itemDetail(item, session);
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView itemDetail(@RequestParam("itemid") Item item, HttpSession session) {
		QuestionForm questionForm = new QuestionForm();
		User user = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		boolean canComplain = false;
		boolean canBuy = false;
		boolean canAsk = false;
		boolean canAnswer = false;
		boolean canDeleteQuestions = false;
		ModelAndView mav = new ModelAndView();

		// Obtiene toda la info del item e incrementa el contador de visitas
		if(item==null){
			mav.setViewName("general/error");
			return mav;
		}
		if(user!=null){
			canComplain = item.canComplain(user);
			canBuy = item.canBuy(user);
			canAsk = item.canAsk(user);
			canAnswer = item.canAnswer(user);
			canDeleteQuestions = item.isOwner(user) || user.isAdmin();
		}
		Auction auction=null;
		if(item.getType().equals(Item.Type.auction)){
			auction = auctionRepository.getAuction(item);
			if(auction.getState().equals(Auction.State.finalized))
				mav.addObject("finalized", true);
			else
				mav.addObject("finalized", false);
		}

		List<Item> suggested = itemRepository.suggestedItems(item);

		mav.addObject("positive", item.getUser().getPositive());
		mav.addObject("neutral", item.getUser().getNeutral());
		mav.addObject("negative", item.getUser().getNegative());
		
		item.incrementVisitCounter();
		mav.addObject("auction", auction);
		mav.addObject("item", item);
		mav.addObject("userL", user);
		mav.addObject("hasComplaint", !canComplain);
		mav.addObject("hasRestriction", !canBuy);
		mav.addObject("canAsk", canAsk);
		mav.addObject("canAnswer", canAnswer);
		mav.addObject("canDeleteQuestions", canDeleteQuestions);
		mav.addObject("items", suggested);
		mav.addObject("questionForm", questionForm);

		mav.setViewName("item/itemDetail");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	public void getImage(@RequestParam("itemid") Item item, HttpServletResponse resp) {
		if(item!=null){
			if (item.getImage()!=null){
				if (item.getImage().getData()!=null){
					if (item.getImage().getData().length!=0){
						try {
							resp.getOutputStream().write(item.getImage().getData());
						} catch (IOException e1) {
							throw new RuntimeException("No se pudo cargar la imagen");
						}
					}
				}
			}
		}
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView purchaseItem(@RequestParam("itemid") Item item) {
		ModelAndView mav = new ModelAndView();
		PurchaseForm purchaseForm = new PurchaseForm();
		purchaseForm.setCount(0);
		int j = item.getCount();
		List<Integer> list = new ArrayList<Integer>();
		for(int i=1; i<=j; i++){
			list.add(i);
		}
		mav.addObject("list", list);
		mav.addObject("itemId", item.getId());
		mav.addObject("purchaseForm", purchaseForm);
		mav.setViewName("item/purchaseItem");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView purchaseItem(HttpSession session, @RequestParam("itemId") Item item,
			PurchaseForm purchaseForm, Errors errors) {
		ModelAndView mav = new ModelAndView();
		int j = item.getCount();
		List<Integer> list = new ArrayList<Integer>();
		for(int i=1; i<=j; i++){
			list.add(i);
		}
		purchaseFormValidator.validate(purchaseForm, errors);
		if(errors.hasErrors()){
			//			purchaseForm.setCount(0);
			mav.addObject("list", list);
			mav.addObject("itemId", item.getId());
			return mav;
		}
		Calendar cal = Calendar.getInstance();
		java.sql.Date jsqlD = new java.sql.Date( cal.getTime().getTime() );
		User user = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		try {
			Purchase purchase = new Purchase(user, item, purchaseForm.getComment(), purchaseForm.getCount(),
					item.getPrecio(), jsqlD);
			purchaseRepository.purchaseItem(purchase);
			item.decrementStock(purchase.getQty());
			mav.addObject("userV", purchase.getItem().getUser());
			mav.addObject("item", item);
			mav.setViewName("user/infoContacto");
			return mav;
		} catch (RestrictionBuyingException e) {
			mav.addObject("errorMsg", e.getMessage());
			mav.setViewName("general/error");
			return mav;
		}
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView offerOnItem(@RequestParam("itemid") Item item) {
		ModelAndView mav = new ModelAndView();
		OfferForm offerForm = new OfferForm();
		Auction au = auctionRepository.getAuction(item);
		if(au.getState().equals(Auction.State.finalized)){
			mav.addObject("errorMsg", "You cannot make an offer on a finalized auction");
			mav.setViewName("general/error");
			return mav;
		}
		mav.addObject("auction", au);
		mav.addObject("item", item);
		mav.addObject("offerForm", offerForm);
		mav.setViewName("item/offerOnItem");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView offerOnItem(HttpSession session, @RequestParam("itemId") Item item,
			@RequestParam("auctionId") Auction auction, OfferForm offerForm, Errors errors) {
		ModelAndView mav = new ModelAndView();
		offerFormValidator.validate(offerForm, errors);
		if(errors.hasErrors()){
			mav.addObject("item", item);
			mav.addObject("auction", auction);
			return mav;
		}
		Calendar cal = Calendar.getInstance();
		java.sql.Timestamp jsqlD = new java.sql.Timestamp( cal.getTime().getTime() );
		User user = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		try {
			Offer offer = new Offer(user, offerForm.getOfferPrice(), jsqlD);
			auction.addOffer(offer);
			mav.addObject("userV", item.getUser());
			mav.addObject("item", item);
			mav.setViewName("user/infoContacto");
			return mav;
		} catch (RestrictionBuyingException e) {
			mav.addObject("errorMsg", e.getMessage());
			mav.setViewName("general/error");
			return mav;
		}
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView finalizeAuction(HttpSession session, @RequestParam("auctionid") Auction auction) {
		ModelAndView mav = new ModelAndView();
		if(auction!=null){
			if(auction.getOffers().size()>0){
				auction.finalizeAuction();
				Offer offer = auction.getMaxOffer();
				try {
					Purchase purchase = new Purchase(offer.getUser(), auction.getItem(), 
							"", auction.getItem().getCount(), offer.getOfferPrice(), new Date());
					purchaseRepository.purchaseItem(purchase);
					auction.getItem().decrementStock(auction.getItem().getCount());
					return itemDetail(auction.getItem(), session);
				} catch (RestrictionBuyingException e) {
					mav.addObject("errorMsg", e.getMessage());
				}
			} else{
				mav.addObject("errorMsg", "You cannot finalize an auction if it does not have any offers");
			}
		}
		mav.setViewName("general/error");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView suspendItem(HttpSession session, @RequestParam("itemid") Item item){
		ModelAndView mav = new ModelAndView();
		User user = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		if(user!=null){
			item.setSuspended(true);
			return itemDetail(item, session);
		}
		mav.setViewName("general/error");
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView unsuspendItem(HttpSession session, @RequestParam("itemid") Item item){
		ModelAndView mav = new ModelAndView();
		User user = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		if(user!=null){
			item.setSuspended(false);
			return itemDetail(item, session);
		}
		mav.setViewName("general/error");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView advancedSearch(){
		ModelAndView mav = new ModelAndView();

		mav.addObject("advancedSearchForm", new AdvancedSearchForm());
		mav.addObject("categories", categoryRepository.getCategories());
		mav.setViewName("item/advancedSearch");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView advancedSearch(AdvancedSearchForm form, Errors errors){
		List<Item> litems=null;
		List<Category> categories = categoryRepository.getCategories();
		ModelAndView mav = new ModelAndView();
		advancedSearchFormValidator.validate(form, errors);
		mav.addObject("categories", categories);
		if (errors.hasErrors()) {
			return mav;
		}
		litems = itemRepository.getAdvancedSearchItems(form.getPattern(), form.getCategory(), form.getMinPrice(), 
				form.getMaxPrice(),form.getState());

		mav.addObject("items",litems);
		mav.setViewName("item/advancedSearch");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView showItemList() {
		ModelAndView mav = new ModelAndView();
		mav.addObject("categories", categoryRepository.getCategories());
		mav.addObject("items", itemRepository.getAllItems(null, -1));
		mav.addObject("action", "../item/showItemList");
		mav.addObject("filterForm", new FilterForm());
		mav.setViewName("item/showItemList");
		return mav;
	}

	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView showItemList(FilterForm filterform, Errors errors) {
		List<Item> litems;
		List<Category> categories;
		ModelAndView mav = new ModelAndView();
		filterFormValidator.validate(filterform, errors);
		if (errors.hasErrors()) {
			return null;
		}
		// No se verifica que exista la categoria porque eso se detecta en el converter
		if (filterform.getCategory() != null) {
			litems = itemRepository.getItemsByCategory(filterform.getCategory(), filterform.getOrderBy(), filterform.getDirection());
		} else {
			litems = itemRepository.getAllItems(filterform.getOrderBy(), filterform.getDirection());
		}
		categories = categoryRepository.getCategories();
		mav.addObject("items",litems);
		mav.addObject("categories",categories);
		mav.addObject("orderBy", filterform.getOrderBy());
		mav.addObject("direction", filterform.getDirection());
		mav.setViewName("item/showItemList");
		return mav;	
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView userItemList(HttpSession session, @RequestParam("userid") User user ) {
		List<Item> litems = itemRepository.getItemsByUser(user, null, -1);
		ModelAndView mav = new ModelAndView();
		mav.addObject("items", litems);
		mav.addObject("action", "../item/userItemList");
		mav.addObject("filterForm", new FilterForm());
		mav.setViewName("item/userItemList");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView ownerItemList(HttpSession session) {
		ModelAndView mav = new ModelAndView();
		User user = (User)session.getAttribute("user");
		List<Item> litems = itemRepository.getItemsByUser(user, null, -1);
		mav.addObject("items", litems);
		mav.addObject("action", "../item/userItemList");
		mav.addObject("filterForm", new FilterForm());
		mav.setViewName("item/ownerItemList");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView DeleteItem(@RequestParam("itemid") Item item){
		itemRepository.deleteItem(item);
		return complaintItemList();
	}

	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView complaintItemList(){
		List<Item> items = itemRepository.getComplaintItems();
		ModelAndView mav = new ModelAndView();
		mav.addObject("items", items);
		mav.setViewName("item/complaintItemList");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView complaintItem(@RequestParam("itemid") Item item, HttpSession session) {
		ModelAndView mav = new ModelAndView();
		if(session.getAttribute("user") instanceof User){
			mav.addObject("item", item);
			mav.setViewName("item/complaintItem");
			return mav;
		}
		return itemDetail(item, session);
	}

	@RequestMapping(method = RequestMethod.POST)
	protected ModelAndView complaintItem(@RequestParam("itemid") Item item,
			@RequestParam("comment") String comment, HttpSession session) {
		User user = null;
		if(session.getAttribute("user") instanceof User){
			user = (User)session.getAttribute("user");
			Complaint complaint = new Complaint(user, comment);
			try {
				item.addComplaint(complaint);
			} catch (ComplaintMyItemException e) {
				ModelAndView mav = new ModelAndView();
				mav.addObject("errorMsg", e.getMessage());
				mav.setViewName("general/error");
				return mav;
			}
		}
		return itemDetail(item, session);
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView listPurchaseByUser(@RequestParam("userId") User user){
		ModelAndView mav = new ModelAndView();
		mav.addObject("purchases", purchaseRepository.getPurchasedAndSellItems(user));
		mav.setViewName("item/listPurchaseByUser");
		return mav;
	}

	@RequestMapping(method = RequestMethod.GET)
	protected ModelAndView unresponsedQuestions(HttpSession session){
		User user = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		
		ModelAndView mav = new ModelAndView();
		if (user!=null){
			mav.addObject("questions",itemRepository.getUnresponsedQuestions(user));
			mav.setViewName("item/unresponsedQuestions");
		}
		else{
			mav.setViewName("general/error");
		}
		return mav;
	}
	
	@RequestMapping(method = RequestMethod.POST)
	protected ModelAndView makeQuestion(HttpSession session,@RequestParam("itemid") Item item,
			QuestionForm questionForm, Errors errors) {
		questionFormValidator.validate(questionForm, errors);
		if(errors.hasErrors()){
			return itemDetail(item, session);
		}
			
		User user = (session.getAttribute("user") instanceof User)?(User)session.getAttribute("user"):null;
		
		if (user!=null){
			Question q=new Question(user,item,new Date(),questionForm.getQuestion());
			item.addQuestion(q);
		}
		
		return itemDetail(item, session);
	}	
	
	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView answer(@RequestParam("questionid") Question q){
		ModelAndView mav = new ModelAndView();
		AnswerForm answerForm=new AnswerForm();
		mav.addObject("q", q);
		mav.addObject("answerForm", answerForm);
		mav.setViewName("item/answer");
		return mav;
	}	
	
	@RequestMapping(method = RequestMethod.POST)
	public ModelAndView answer(HttpSession session,@RequestParam("questionid") Question q,AnswerForm answerForm, Errors errors){
		answerFormValidator.validate(answerForm, errors);
		ModelAndView mav = new ModelAndView();
		if(errors.hasErrors()){
			mav.addObject("q", q);
			return mav;
		}
		
		q.setAnswer(answerForm.getAnswer());
		return itemDetail(q.getItem(), session);
	}
	
	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView deleteQuestion(@RequestParam("questionid") Question q, HttpSession session){
		Item item=q.getItem();
		item.removeQuestion(q);
		return itemDetail(item, session);
	}	
}